﻿// @brief
// @file Peripheral.cs
// @date 2024年08月17日
// @author doublecat
// 
// Copyright (C) &lt;2024&gt;  &lt;doublecat&gt;
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using System.Globalization;
using System.Xml.Serialization;

namespace doublecat.mcu.svd;

/// <summary>
/// 片上设备
/// </summary>
public class Peripheral : IRegisterGroup, IDimGroup {
    private uint        _baseAddress;
    private Peripheral? _derivedFrom; // 继承的peripheral对象
    private uint?       _size;

    /// <summary>
    /// 构造函数
    /// </summary>
    public Peripheral() : this("", "0x00000000") {
    }

    /// <summary>
    /// 构造一个新的Peripheral对象
    /// </summary>
    /// <param name="name">名称</param>
    /// <param name="baseAddress">地址</param>
    public Peripheral(string name, string baseAddress) {
        Name        = name;
        BaseAddress = baseAddress;
    }

    /// <summary>
    /// 中断
    /// </summary>
    [XmlElement("interrupt")]
    public List<Interrupt> Interrupts { get; set; } = new List<Interrupt>(); // Note: 使用XmlElement生成一个非嵌套的序列

    /// <summary>
    /// 寄存器
    /// </summary>
    [XmlArray("registers")]
    [XmlArrayItem("register")]
    public List<Register> Registers { get; set; } = new List<Register>();

    /// <summary>
    /// 地址块
    /// </summary>
    [XmlElement("addressBlock")]
    public List<AddressBlock> AddressBlocks { get; set; } = new List<AddressBlock>();

    /// <summary>
    /// 名称
    /// </summary>
    [XmlElement("name")]
    public string Name { get; set; }

    /// <summary>
    /// 版本
    /// </summary>
    [XmlElement("version")]
    public string? Version { get; set; }

    /// <summary>
    /// 设备描述
    /// </summary>
    [XmlElement("description")]
    public string? Description { get; set; }

    /// <summary>
    /// 同一地址块上的候选设备
    /// </summary>
    [XmlElement("alternatePeripheral")]
    public string? AlternatePeripheral { get; set; }

    /// <summary>
    /// 用于系统视图显式用的名称
    /// </summary>
    [XmlElement("groupName")]
    public string? GroupName { get; set; }

    /// <summary>
    /// 用于寄存器前缀的名称
    /// </summary>
    [XmlElement("prependToName")]
    public string? PrependToName { get; set; }

    /// <summary>
    /// 附加到寄存器后缀的名称
    /// </summary>
    [XmlElement("appendToName")]
    public string? AppendToName { get; set; }

    /// <summary>
    /// C结构名称，当此项不为空时，使用此项作为结构名称
    /// </summary>
    [XmlElement("headerStructName")]
    public string? HeaderStructName { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("disableCondition")]
    public string? DisableCondition { get; set; }

    /// <summary>
    /// 
    /// </summary>
    [XmlElement("baseAddress", DataType = "nonNegativeInteger")]
    public required string BaseAddress {
        get => $"0x{_baseAddress:X8}";
        set => _baseAddress =
            value.StartsWith("0x")
                ? uint.Parse(value.Remove(0, 2), NumberStyles.HexNumber)
                : uint.Parse(value);
    }

    /// <summary>
    /// peripheral是否从其他设备继承
    /// </summary>
    [XmlAttribute("derivedFrom")]
    public string? DerivedFrom { get; set; }

    /// <summary>
    /// 父设备
    /// </summary>
    public required Device Parent { get; set; }

    #region IDimGroup Members

    [XmlElement("dim")]
    public string? Dim { get; set; }

    [XmlElement("dimIncrement")]
    public string? DimIncrement { get; set; }

    [XmlElement("dimIndex")]
    public string? DimIndex { get; set; }

    [XmlElement("dimName")]
    public string? DimName { get; set; }

    [XmlElement("dimArrayIndex")]
    public DimArrayIndex? ArrayIndex { get; set; }

    #endregion

    #region IRegisterGroup Members

    [XmlElement("size")]
    public string? Size {
        get => _size.ToString();
        set => _size = value.ToUInt();
    }

    [XmlElement("access")]
    public string? Access { get; set; }

    [XmlElement("protection")]
    public string? Protection { get; set; }

    [XmlElement("resetValue")]
    public string? ResetValue { get; set; }

    [XmlElement("resetMask")]
    public string? ResetMask { get; set; }

    #endregion

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public uint GetSize() {
        return _size ?? Parent.GetSize();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public uint GetBaseBassress() {
        return _baseAddress;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public Register? FindRegister(string name) {
        return Registers.Find(r => r.Name == name);
    }

    /// <summary>
    /// 寄存器的保留空间的内插
    /// </summary>
    public List<Register> RegistersInterpolation() {
        List<Register> registers = Registers.OrderBy(r => r.AddressOffset).ToList();
        Registers.Clear();
        // 地址计数器清零
        uint offset = 0;
        // 循环每一个寄存器
        foreach (Register? register in registers) {
            uint raddr = register.GetAddressOffset();
            // 检查当前寄存器的地址是否等于地址计数器
            if (raddr > offset) {
                // 如果不等于，则插入n个保留寄存器， n = （当前寄存器的地址 - 地址计数器）/ 寄存器大小
                uint n = raddr - offset;
                Register rr = new Register {
                    Name          = $"RESERVED_OFFSET_0x{offset:X8}[%s]",
                    AddressOffset = Convert.ToString(offset),
                    Dim           = n.ToString(),
                    DataType      = "uint8_t",
                    Size          = "8",
                    Parent        = this
                };
                Registers.Add(rr);
                // 更新地址计数器
                offset += n;
            }
            // 寄存器是否是一个列表
            if (register.Dim != null && register.GetDim() > 0) {
                uint rs = register.GetSize() / 8 * register.GetDim();
                offset += rs;
            } else {
                offset += register.GetSize() / 8;
            }
            Registers.Add(register);
        }
        return Registers;
    }

    /// <summary>
    /// 返回设备名称
    /// </summary>
    /// <returns></returns>
    public string GetName() {
        return HeaderStructName ?? DimName ?? Name;
    }

    #region Nested type:  AddressBlock

    public class AddressBlock {
        [XmlElement("offset")]
        public required string Offset { get; set; }

        [XmlElement("protection")]
        public ProtectionEnum? Protection { get; set; }

        [XmlElement("size")]
        public required string Size { get; set; }

        [XmlElement("usage")]
        public required string Usage { get; set; }
    }

    #endregion

    #region Nested type:  Interrupt

    /// <summary>
    /// 设备中断
    /// </summary>
    /// <remarks>
    /// todo 有公用中断的情况：例如nrf51 power和clock公用一个中断
    /// </remarks>
    public class Interrupt {
        /// <summary>
        /// 名称
        /// </summary>
        [XmlElement("name")]
        public required string Name { get; set; }

        /// <summary>
        /// 模式
        /// </summary>
        [XmlElement("description")]
        public string? Description { get; set; }

        /// <summary>
        /// 中断的索引值
        /// </summary>
        [XmlElement("value")]
        public required uint Value { get; set; }
    }

    #endregion
}

/// <summary>
/// 
/// </summary>
public enum UsageEnum {
    /// <summary>
    /// 
    /// </summary>
    Registers,

    /// <summary>
    /// 
    /// </summary>
    Buffer,

    /// <summary>
    /// 
    /// </summary>
    Reserved
}

/// <summary>
/// 地址块帮助类
/// </summary>
public static class AddressBlockHelper {
    /// <summary>
    /// 将一个字符串转换成Usage枚举;
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static UsageEnum? ToUsageEnum(this string str) {
        return str switch {
            "registers" => UsageEnum.Registers,
            "buffer"    => UsageEnum.Buffer,
            "reserved"  => UsageEnum.Reserved,
            var _       => null
        };
    }

    /// <summary>
    /// 返回Usage枚举名称;
    /// </summary>
    /// <param name="usage"></param>
    /// <returns></returns>
    public static string? GetName(this UsageEnum? usage) {
        return usage == null ? null : Enum.GetName(usage.Value);
    }
}
